package chatserver.netio;


import chatserver.logging.ServerLog;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.net.Socket;
import java.util.Date;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;


public class Connection {

    private int pingTime;    
    private final Queue<byte[]> cmdQueue;
    private Thread readerThread;
    private Thread writerThread;
    private boolean closed;
    private String socketInfo;
    private String clientIp;
    private Socket socket;
    private String tmpName;
    private String tmpDigestSalt;
    private BufferedInputStream bufferedinputstream;
    private BufferedOutputStream bufferedoutputstream;
    private EventHandler listener;
    private long connectionTime;
    
    
    public Connection(Socket s, BufferedInputStream in, BufferedOutputStream out)
            throws Exception {
        pingTime = 0x1d4c0;

        this.connectionTime = new Date().getTime();
        this.bufferedinputstream = in;
        this.bufferedoutputstream = out;
        this.listener = new EventHandler(this);
        this.socketInfo = s.getInetAddress().getHostAddress() + ":" + s.getPort();
        this.clientIp = s.getInetAddress().getHostAddress();
        this.tmpDigestSalt = null;
        this.tmpName = null;
        this.closed = false;

        cmdQueue = new ConcurrentLinkedQueue<byte[]>();
        socket = s;

        readerThread = new Thread(new Runnable() {

            @Override
            public void run() {
                reader();
            }
        });
        readerThread.start();
        writerThread = new Thread(new Runnable() {

            @Override
            public void run() {
                writer();
            }
        });
        writerThread.start();
    }

    public byte[] getRawIp()
    {
        return socket.getInetAddress().getAddress();
    }
    
    public long getConnectionTime()
    {
       return connectionTime;        
    }
    
    public EventHandler getEventHandler()
    {
        return listener;
    }

    
    public synchronized void close() {
        if (!closed) {
            try {
                socket.close();
                socket = null;
            } catch (Exception e) {
            }
            try {
                listener.connectionClosed(this);
            } catch (Exception e) {
                e.printStackTrace();
            }
            closed = true;
        }
    }

    protected boolean isClosed() {
        return closed;
    }

    public void destroy() {
        close();
        while (readerThread != null && readerThread != Thread.currentThread() && readerThread.isAlive()) {
            try {
                readerThread.interrupt();
                readerThread.join();
            } catch (InterruptedException e) {
            }
        }
        while (writerThread != null && writerThread != Thread.currentThread() && writerThread.isAlive()) {
            try {
                writerThread.interrupt();
                writerThread.join();
            } catch (InterruptedException e) {
            }
        }
    }

    public void waitForCmd()
            throws Exception {
        synchronized (cmdQueue) {
            for (; cmdQueue.size() == 0; cmdQueue.wait()) {}
        }
    }

    public byte[] getCmd()
            throws Exception {
        byte[] abyte0;
        synchronized (this.cmdQueue) {
            while (this.cmdQueue.size() == 0) {
                try {
                    this.cmdQueue.wait(this.pingTime);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            abyte0 = (byte[]) this.cmdQueue.poll();
        }

        return abyte0;
    }

    public byte[] tryCmd()
            throws Exception {
        byte abyte0[];
        synchronized (cmdQueue) {
            if (cmdQueue.isEmpty()) {
                abyte0 = (byte[]) null;
            } else {
                abyte0 = (byte[]) cmdQueue.poll();
            }
        }
        return abyte0;
    }

    public void sendCommand(Command command)
    {
        ServerLog.log("ADZ - Connection.java(Server side)->sendCommand::"+command);
        synchronized (cmdQueue) {
            try {
                ServerLog.log(this, "sent " + command.toString());
                
                ServerLog.log("ADZ - Connection.java(Server side)->sendCommand:command.getByteCache(true)=:"+command.getByteCache(true));
                cmdQueue.add(command.getByteCache(true));
                cmdQueue.notify();
            } catch (Exception e) {
                close();
            }
        }
    }

    
    private void reader() {
        try {
            while (!closed) {
                Event event = new Event(bufferedinputstream, this);
                try {
                    listener.doEvent(event);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        } catch (Exception e) {
            close();
        }
    }

    private void writer() {
        ServerLog.log("ADZ - Connection.java(Server side)->writer");
        try {

            for (; !closed; bufferedoutputstream.flush()) {
                byte abyte0[] = getCmd();
                bufferedoutputstream.write(abyte0);
                while ((abyte0 = tryCmd()) != null) {
                    bufferedoutputstream.write(abyte0);
                }
                
            }

        } catch (Exception e) {
            close();
        }
    }

    public String getSocketInfo() {
        return socketInfo;
    }
    
    public String getClientIp()
    {
        return clientIp;
    }
    
    public void setTmpLoginData(String name, String digestSalt)
    {
        tmpName = name;
        tmpDigestSalt = digestSalt;
    }
    
    public String getTmpLoginName()
    {
        return tmpName;
    }
    
    public String getTmpDigestSalt()
    {
        return tmpDigestSalt;
    }
 
   
}
